package com.xiaozhu.acsp;

import com.xiaozhu.anno.RolePermission;
import com.xiaozhu.utils.BaseContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 权限认证切面
 * 通过环绕通知，对带有 @RolePermission 注解的方法进行权限校验
 *
 * 原理：
 * Spring MVC 在处理请求时会把当前请求信息绑定到 ThreadLocal 中。
 * RequestContextHolder 用来获取当前请求的 HttpServletRequest 和 HttpServletResponse。
 */
@Aspect
@Component
@Slf4j
public class MyAdvice {


    /**
     * 环绕通知
     * @param joinPoint 切点
     * @param rolePermission 注解对象
     * @return 方法返回值或 null（当权限不足时）
     * @throws Throwable
     */
    @Around("@annotation(rolePermission)")
    public Object checkRole(ProceedingJoinPoint joinPoint,
                            RolePermission rolePermission) throws Throwable {

        Boolean flag = false;

        log.info("切面：开始进行权限认证");
        
        // 初始管理员不要权限认证，
        Long threadId = BaseContext.getThreadId();
        if (threadId == 1) {
            log.info("初始管理员认证通过");
            return joinPoint.proceed();
        }


        //  ServletRequestAttributes 获取当前请求对象: 获取当前 HTTP 请求相关信息的工具类
        // ServletRequestAttribute: 是RequestAttributes的一个实现类
        /*
        作用：
            保存 当前线程的请求上下文（RequestAttributes）。
            支持在任何地方访问 HttpServletRequest、HttpServletResponse 和 HttpSession。
            线程绑定：每个请求都会绑定到当前线程，因此可以在请求生命周期内获取相关信息。
         */
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        HttpServletResponse response = servletRequestAttributes.getResponse();
        HttpServletRequest request = servletRequestAttributes.getRequest();


        log.info("当前请求URL：{}", request.getRequestURI());
        log.info("请求方式：{}", request.getMethod());

        // 从请求属性中获取权限列表
        // 是我们在拦截器中设置的
        Object perms = request.getAttribute("perms");

        // 未登录或权限为空
        if (perms == null) {
            log.warn("未登录或未分配权限");
            return buildResponse_xz(response, "没有权限", 403);
        }

        // 统一处理权限集合，使用结合Collection可以兼容 List 和 Set
        /*
        为什么要用 Collection<?> 而不是 Collection<String>
        有些权限框架返回的集合类型不固定（可能是 Set、List 等），或元素类型不确定。
        使用 <?> 可以避免类型转换错误，同时在遍历时需要先做类型判断或强转。
         */
        Collection<?> permsCollection;
        if (perms instanceof Collection) {
            // 进行强制转换
            permsCollection = (Collection<?>) perms;
        } else {
            // 不是集合类型，直接报错
            return buildResponse_xz(response, "权限格式错误", 403);
        }

        // 转换成标准化 List<String>（去空格、小写）
        List<String> permsList = permsCollection.stream()
                /*
                    为什么要：.map(Object::toString)
                    输入：permsCollection 是一个 Collection<?>，元素类型可能不是 String，可能是 Integer、Enum、自定义对象等。
                    作用：将集合中的每个元素都调用 toString() 方法，转换成 String
                    结果：生成一个 字符串流，保证后续 .trim() 和 .toLowerCase() 可以调用，因为它们都是 String 的方法。
                 */
                .map(Object::toString)
                .map(String::trim)
                .map(String::toLowerCase)
                .collect(Collectors.toList());

        log.info("JWT解析出的权限列表 permsList: {}", permsList);

        // 注解中定义的权限 需要获取注解的url()
        String[] annotationPermissions = rolePermission.url();
        log.info("annotationPermissions: {}", Arrays.toString(annotationPermissions));
        // 获取方法名称进行拼接
        String methodName = request.getMethod().toLowerCase();

        // 方法1：
        for(String ap : annotationPermissions ){
            String currentPerm = methodName + ":" + ap;
            log.info("current perm: {}", currentPerm);
            // 最关键
            if (permsList.contains(currentPerm)) {
                flag = true;
                break;
            }
        }

        /*: 方式二
        String[] requiredPerms = new String[annotationPermissions.length];
        for (int i = 0; i < annotationPermissions.length; i++) {
            requiredPerms[i] = methodName + ":" + annotationPermissions[i].trim().toLowerCase();
        }

        boolean hasPermission = Arrays.stream(requiredPerms)
        .map(String::trim)
        .map(String::toLowerCase)
        //permsList.contains(ap)最关键
        .anyMatch(ap ->permsList.contains(ap));
         */

        if (!flag) {
            log.warn("权限不足: {}", Arrays.toString(annotationPermissions));
            return buildResponse_xz(response, "没有权限", 403);
        }

        log.info("权限认证通过");
        // 执行目标方法
        return joinPoint.proceed();
    }

    /**
     * 响应封装
     * @param response HttpServletResponse
     * @param info 返回信息
     * @param status HTTP状态码
     * @return null
     */
    private Object buildResponse_xz(HttpServletResponse response, String info, int status) {
        try {
            if (response != null) {
                response.setStatus(status);
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write(info);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }
}
